﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using SimpleExpression;
using System.Collections.ObjectModel;
using System.Collections;

namespace SimpleExpression
{
    public class XMLSelectCommandStrategy : SelectCommandStrategy, ISelectCommandStrategy
    {
        public XMLSelectCommandStrategy()
            :base(new XMLProvider())
        {
        }

        public override void AddAttribute(AttributeExpression expression)
        {
            string propertyName = GetPropertyName(expression);
            
            this.selectClause.AppendLine();
            this.selectClause.AppendFormat("<attribute alias={0} name={1}/>", this.provider.Encode(expression.PropertyMetadata.EntityMetadata.Alias), this.provider.Encode(propertyName));
            this.attributesAliases.Add(propertyName);
        }

        public override void AddCondition(ConditionExpression expression)
        {
            this.whereClause.AppendLine();
            this.whereClause.Append("<condition");
            this.whereClause.Append(SPACE);

			if (expression.Value1.GetType() == typeof(PropertyMetadata))
			{
				this.whereClause.AppendFormat(" attribute-alias=\"{0}\" ", ((PropertyMetadata)expression.Value1).Alias);
			}

			
            this.whereClause.AppendFormat("attribute={0}", this.GetConditionValue(expression, expression.Value1));

            this.whereClause.Append(SPACE);

            this.whereClause.AppendFormat("condition-operator={0}", this.provider.Encode(expression.GetSQLConditionOperator()));
            
            this.whereClause.Append(SPACE);

			if (expression.Value2.GetType() == typeof(PropertyMetadata))
			{
				this.whereClause.AppendFormat(" value-alias=\"{0}\" ", ((PropertyMetadata)expression.Value2).Alias);
			}
            this.whereClause.AppendFormat("value={0}", this.GetConditionValue(expression, expression.Value2));

            this.whereClause.Append(SPACE);

            this.whereClause.AppendFormat("logical-operator={0} />", this.provider.Encode(expression.LogicalOperator.ToString()));
        }

        private string GetConditionValue(ConditionExpression expression, object value)
        {
            PropertyMetadata attribute;

            if ((attribute = value as PropertyMetadata) != null)
                return this.provider.Encode(string.Format("{1}", attribute.Alias, attribute.PhysicalName));
            else
                return this.provider.Encode(expression.GetValue(value));
        }

        public override void AddLinkEntity(LinkEntityExpression linkEntity)
        {
            this.joinClause.AppendLine();
            this.joinClause.AppendFormat("<linkentity joinoperator={0} schema={1} entity={2} alias={3} >", this.provider.Encode(linkEntity.JoinOperator.ToString()), this.provider.Encode(linkEntity.EntityTo.Schema), this.provider.Encode(linkEntity.EntityTo.PhysicalName), this.provider.Encode(linkEntity.EntityTo.Alias));
            this.joinClause.Append(this.whereClause.ToString());
            this.joinClause.AppendLine();
            this.joinClause.Append("</linkentity>");

            this.whereClause.Length = 0;
        }

        public override void AddDistinctClause(EntityExpression expression)
        {
            
        }

        public override void AddFromClause(EntityExpression expression)
        {
            this.fromClause.AppendLine();
            this.fromClause.AppendFormat("<entity schema={0} name={1} alias={2} >", this.provider.Encode(expression.EntityMetadata.Schema), this.provider.Encode(expression.EntityMetadata.PhysicalName), this.provider.Encode(expression.EntityMetadata.Alias));
            this.fromClause.Append("{0}");
            this.fromClause.Append(Environment.NewLine);
            this.fromClause.Append("</entity>");
        }

        public override void BeginFilterExpression(FilterExpression expression)
        {
            this.whereClause.AppendLine();
            this.whereClause.AppendFormat("<filter logical-operator={0} >", this.provider.Encode(expression.LogicalOperator.ToString()));
        }

        public override void AddWhereClause(FilterExpression expression)
        {
            //TODO: Review this method
        }

        public override void EndFilterExpression(FilterExpression expression)
        {
            this.whereClause.AppendLine();
            this.whereClause.Append("</filter>");
        }

        public override void AddGroupByClause(GroupByExpression expression)
        {
            this.groupByClause.AppendLine();
            this.groupByClause.Append("<groupby>");

            foreach (AttributeExpression item in expression.Attributes)
            {
                this.groupByClause.AppendLine();
                this.groupByClause.AppendFormat("<attribute alias={0} name={1} />", this.provider.Encode(item.PropertyMetadata.Alias), this.provider.Encode(item.PropertyMetadata.PhysicalName));
            }

            this.groupByClause.AppendLine();
            this.groupByClause.Append("</groupby>");
        }

        public override void AddOrderClause(OrderExpression expression)
        {
            this.orderByClause.AppendLine();
            this.orderByClause.Append("<orderby>");


            foreach (KeyValuePair<AttributeExpression, OrderType> item in expression.Properties)
            {
                this.orderByClause.AppendLine();
                this.orderByClause.AppendFormat("<order alias={0} name={1} type={2} />", this.provider.Encode(item.Key.PropertyMetadata.Alias), this.provider.Encode(item.Key.PropertyMetadata.PhysicalName), this.provider.Encode(item.Value.ToString()));
            }

            this.orderByClause.AppendLine();
            this.orderByClause.Append("</orderby>");
        }

        public override void AddPagingClause(PagingExpression expression)
        {
            //Not implemented
        }

        protected override string GetCommandText()
        {
            StringBuilder builder = new StringBuilder();

            builder.Append(this.selectClause.ToString());
            builder.Append(this.joinClause.ToString());
            builder.Append(this.whereClause.ToString());
            builder.Append(this.groupByClause.ToString());
            builder.Append(this.orderByClause.ToString());

            string aux = String.Format(this.fromClause.ToString(), builder.ToString());

            builder.Length = 0;
            builder.Append("<fetch>");
            builder.Append(aux);
            builder.Append(Environment.NewLine);
            builder.Append("</fetch>");

            return builder.ToString();
        }
       
        public override void WriteCommand()
        {
            this.command = GetCommandText();
        }

        private string GetPropertyName(AttributeExpression attribute)
        {
            if (!(attribute is ViewAttributeExpression))
                return attribute.PropertyMetadata.PhysicalName;
            else
                return attribute.PropertyMetadata.PlatformName;
        }
    }
}
